/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package neuralnetwork;

/**
 * 神经网络核心类 实现一个全连接的前馈神经网络，支持任意数量的隐藏层
 *
 * @author 朱婧雯
 */
import java.util.Random;

public class NeuralNetwork {

    private Layer[] layers;
    private Random random;
    private int inputSize;
    private int hiddenSize;
    private int outputSize;
    private int hiddenLayers;

    /**
     * 神经网络构造函数
     *
     * @param inputSize 输入层神经元数量
     * @param hiddenSize 每个隐藏层的神经元数量
     * @param outputSize 输出层神经元数量
     * @param hiddenLayers 隐藏层的层数
     * @throws IllegalArgumentException 如果网络结构参数无效
     *
     */
    public NeuralNetwork(int inputSize, int hiddenSize, int outputSize, int hiddenLayers) {
        // 参数验证
        if (inputSize <= 0) {
            throw new IllegalArgumentException("输入层神经元数量必须大于0");
        }
        if (hiddenSize < 0) {
            throw new IllegalArgumentException("隐藏层神经元数量不能为负数");
        }
        if (outputSize <= 0) {
            throw new IllegalArgumentException("输出层神经元数量必须大于0");
        }
        if (hiddenLayers < 0) {
            throw new IllegalArgumentException("隐藏层层数不能为负数");
        }
        if (hiddenLayers > 0 && hiddenSize == 0) {
            throw new IllegalArgumentException("有隐藏层时，隐藏层神经元数量必须大于0");
        }

        this.inputSize = inputSize;
        this.hiddenSize = hiddenSize;
        this.outputSize = outputSize;
        this.hiddenLayers = hiddenLayers;
        this.random = new Random();

        initializeLayers();

        System.out.printf("神经网络创建成功: %d-%d(x%d)-%d (总参数: %d)%n",
                inputSize, hiddenSize, hiddenLayers, outputSize, getTotalParameters());
    }

    /**
     * 初始化网络各层
     *
     * @throws IllegalStateException 如果层初始化失败
     */
    private void initializeLayers() {
        // 总层数 = 隐藏层 + 输出层（输入层不计算在内）
        layers = new Layer[hiddenLayers + 1];

        try {
            if (hiddenLayers > 0) {
                layers[0] = new Layer(hiddenSize, inputSize, random);

                for (int i = 1; i < hiddenLayers; i++) {
                    layers[i] = new Layer(hiddenSize, hiddenSize, random);
                }

                layers[hiddenLayers] = new Layer(outputSize, hiddenSize, random);
            } else {
                layers[0] = new Layer(outputSize, inputSize, random);
            }
        } catch (Exception e) {
            throw new IllegalStateException("网络层初始化失败: " + e.getMessage(), e);
        }
    }

    /**
     * 前向传播计算
     *
     * @param inputs 输入数据数组，长度必须等于inputSize
     * @return 网络输出数组，长度等于outputSize
     * @throws IllegalArgumentException 如果输入数据无效
     */
    public double[] forward(double[] inputs) {
        // 输入验证
        if (inputs == null) {
            throw new IllegalArgumentException("输入数据不能为null");
        }
        if (inputs.length != inputSize) {
            throw new IllegalArgumentException(
                    String.format("输入数据维度错误: 期望%d, 实际%d", inputSize, inputs.length));
        }
        double[] currentOutput = inputs;
        for (Layer layer : layers) {
            currentOutput = layer.forward(currentOutput);
        }

        return currentOutput;
    }

    /**
     * 获取网络所有层
     *
     * @return 层数组，不包含输入层
     */
    public Layer[] getLayers() {
        return layers;
    }

    /**
     * 获取网络各层的大小信息
     *
     * @return 各层神经元数量数组，格式：[输入层, 隐藏层1, ..., 输出层]
     *
     */
    public int[] getLayerSizes() {
        // 数组大小 = 输入层 + 隐藏层 + 输出层
        int[] sizes = new int[hiddenLayers + 2];
        sizes[0] = inputSize; // 输入层

        // 填充隐藏层和输出层
        for (int i = 0; i <= hiddenLayers; i++) {
            sizes[i + 1] = layers[i].getNeuronCount();
        }

        return sizes;
    }

    /**
     * 获取网络总参数数量
     *
     * @return 总参数数量（权重数 + 偏置数）
     *
     */
    public int getTotalParameters() {
        int total = 0;
        for (Layer layer : layers) {
            for (Neuron neuron : layer.neurons) {
                total += neuron.weights.length + 1; // 权重数 + 偏置
            }
        }
        return total;
    }

    /**
     * 获取所有参数作为一维数组
     *
     * @return 包含所有权重和偏置的一维数组
     */
    public double[] getAllParameters() {
        double[] params = new double[getTotalParameters()];
        int index = 0;

        for (Layer layer : layers) {
            for (Neuron neuron : layer.neurons) {
                // 复制权重
                for (double weight : neuron.weights) {
                    params[index++] = weight;
                }
                // 复制偏置
                params[index++] = neuron.bias;
            }
        }

        return params;
    }

    /**
     * 设置所有网络参数
     *
     * @param params 包含所有权重和偏置的一维数组
     * @throws IllegalArgumentException 如果参数数组长度不匹配
     */
    public void setAllParameters(double[] params) {
        int expectedLength = getTotalParameters();
        if (params.length != expectedLength) {
            throw new IllegalArgumentException(
                    String.format("参数数组长度不匹配: 期望%d, 实际%d", expectedLength, params.length));
        }

        int index = 0;

        for (Layer layer : layers) {
            for (Neuron neuron : layer.neurons) {
                // 设置权重
                double[] newWeights = new double[neuron.weights.length];
                for (int i = 0; i < neuron.weights.length; i++) {
                    newWeights[i] = params[index++];
                }
                // 设置偏置
                double newBias = params[index++];
                neuron.setWeightsAndBias(newWeights, newBias);
            }
        }
    }

    /**
     * 获取网络结构描述
     *
     * @return 网络结构的字符串描述
     */
    public String getNetworkStructure() {
        int[] sizes = getLayerSizes();
        StringBuilder sb = new StringBuilder();
        sb.append(sizes[0]);
        for (int i = 1; i < sizes.length; i++) {
            sb.append("-").append(sizes[i]);
        }
        return sb.toString();
    }

    /**
     * 获取网络配置信息
     *
     * @return 包含网络详细配置的字符串
     */
    @Override
    public String toString() {
        return String.format(
                "NeuralNetwork[结构=%s, 参数=%d, 隐藏层=%d]",
                getNetworkStructure(), getTotalParameters(), hiddenLayers
        );
    }

    // Getter方法用于访问网络配置
    /**
     * 获取输入层大小
     *
     * @return 输入层神经元数量
     */
    public int getInputSize() {
        return inputSize;
    }

    /**
     * 获取隐藏层大小
     *
     * @return 每个隐藏层的神经元数量
     */
    public int getHiddenSize() {
        return hiddenSize;
    }

    /**
     * 获取输出层大小
     *
     * @return 输出层神经元数量
     */
    public int getOutputSize() {
        return outputSize;
    }

    /**
     * 获取隐藏层层数
     *
     * @return 隐藏层的数量
     */
    public int getHiddenLayers() {
        return hiddenLayers;
    }
}
